#include "CentralCache.h"
#include "../PageCache/PageCache.h"

extern ObjectPool<Span> pool_for_spanlist;
CentralCache CentralCache::_sInt; // 定义实例对象

CentralCache *CentralCache::GetInstance()
{
    return &_sInt;
}

CentralCache::CentralCache()
{
    //对管理的所有SpanList对象延迟初始化
    for(size_t i = 0;i < NFREELISTS;i++)
    {
        _spanLists[i].DelayInitialize(pool_for_spanlist);
    }
}

Span *CentralCache::GetSpan(SpanList &spanList, size_t alignSize, size_t batchNum)
{
    // 查看当前桶下是否存在可以使用的Span
    Span *it = spanList.Begin();
    while (it != spanList.End())
    {
        if (it->_freeList != nullptr)
            return it;
        else
            it = it->_next;
    }

    // 因为接下来的一部分不涉及该spanList,释放锁
    spanList.GetMutex().unlock();
    //走到这里说明没有空闲span,需要找PageCache获取
    //访问前先加锁
    PageCache::GetInstance()->GetMutex().lock();
    // Span *span = PageCache::GetInstance()->NewSpan(SizeClass::NumMovePage(alignSize,batchNum)); // 获得一个新Span
    Span *span = PageCache::GetInstance()->NewSpan(SizeClass::NumMovePage(alignSize)); // 获得一个新Span
    PageCache::GetInstance()->GetMutex().unlock();


    // 计算Span的大块内存的起始地址和大块内存的大小(字节数),进行切分,构建span的自由链表
    // 切分过程不需要加锁,因为切分涉及资源是线程独享的
    span->_alignSize = alignSize;
    char *p1 = (char *)(span->_pageId << PAGE_SHIFT); // 8KB为一页
    size_t bytes = span->_n << PAGE_SHIFT;            // 内存空间字节总数
    char *end = p1 + bytes;                         // 整块内存终点
    span->_freeList = (void *)p1;
    char *p2 = p1 + alignSize;
    while (p2 < end)
    {
        FreeList::GetPtr(p1) = p2;
        p1 = p2;
        p2 += alignSize;
    }   
    FreeList::GetPtr(p1) = nullptr;//freelist表结构以nullptr结尾
    // 切分完成,将新获取的span添加到相应桶下的SpanList中:需要加锁
    spanList.GetMutex().lock();
    spanList.PushFront(span);
    return span;
}
size_t CentralCache::FetchRangeObj(void *&start, void *&end, size_t alignSize, size_t batchNum, size_t index)
{
    // 访问特定的桶,加上桶锁
    _spanLists[index].GetMutex().lock();
    Span *span = GetSpan(_spanLists[index], alignSize, batchNum); // 获取一个相应的Span
    assert(span);
    assert(span->_freeList);

    start = span->_freeList;
    end = start;
    size_t actualNum = 1;//注意此处的数字,至少拿一个
    while (actualNum < batchNum && FreeList::GetPtr(end) != nullptr) 
    {
        end = FreeList::GetPtr(end);
        actualNum++;
    }
    span->_freeList = FreeList::GetPtr(end);
    FreeList::GetPtr(end) = nullptr;
    span->_useCount += actualNum;
    _spanLists[index].GetMutex().unlock();
    return actualNum;
}

void CentralCache::ReleaseToCentral(void* start,size_t index)
{
    //先计算在哪个桶中:但是在这个桶的span列表中,内存是属于哪个span呢
    //通过span的id和n来进行判断
    _spanLists[index].GetMutex().lock();
    while (start)
    {
        Span* span = PageCache::GetInstance()->MapObjectToSpan(start);
        void* next = FreeList::GetPtr(start);
        //头插
        FreeList::GetPtr(start) = span->_freeList;
        span->_freeList = start;
        span->_useCount--;
        
        //如果span的_useCount为0:说明span切分出去的小块内存都已经归还
        //这个span就可以再将内存交换给PageCache:进一步做前后页合并
        if(span->_useCount == 0)
        {
            _spanLists[index].Erase(span);
            //相关清理工作
            span->_freeList = nullptr;
            span->_next = nullptr;
            span->_prev = nullptr;
            //将span由CentralCache中归还PageCache
            _spanLists[index].GetMutex().unlock();
            PageCache::GetInstance()->GetMutex().lock();
            PageCache::GetInstance()->ReleaseToPage(span);            
            PageCache::GetInstance()->GetMutex().unlock();
            _spanLists[index].GetMutex().lock();//再次加锁
        }
        start = next;
    }
    
    _spanLists[index].GetMutex().unlock();
}
